package main

// only zip achive all file.
// align to bound 4 bytes

import (
	"archive/zip"
	"errors"
	"fmt"
	"hash"
	"io"
)

type manifestEntry struct {
	name string
	sha1 hash.Hash
}

type countWriter struct {
	w     io.Writer
	count int64
}

func (w *countWriter) Write(p []byte) (int, error) {
	n, err := w.w.Write(p)
	w.count += int64(n)
	return n, err
}

type Zapk struct {
	zw *zip.Writer
	fp *countWriter
}

func NewApkWriter(fws io.WriteSeeker) *Zapk {
	fp := &countWriter{fws, 0}
	zk := &Zapk{fp: fp}
	zk.zw = zip.NewWriter(fp)
	return zk
}

func (zk *Zapk) create(fname string) (io.Writer, error) {
	const fileHeaderLen = 30 // + filename + extra

	/**/
	zk.zw.Flush()
	ofs := zk.fp.count + fileHeaderLen + int64(len(fname))
	tsiz := int(-ofs) & 0x3
	fmt.Printf("align, ofs = %x, pad = %v\n", ofs, tsiz)

	hdr := &zip.FileHeader{Name: fname, Method: zip.Store, Extra: make([]byte, tsiz)}
	dst, err := zk.zw.CreateHeader(hdr)
	if err != nil {
		return nil, err
	}

	return dst, nil
}

func (zk *Zapk) AddFromFile(fname string, src io.Reader) error {

	/**/
	dst, err := zk.create(fname)
	if err != nil {
		return err
	}

	/**/
	buf := make([]byte, 32768)

	for {
		nr, er := src.Read(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw < 0 || nr < nw {
				nw = 0
				if ew == nil {
					return errors.New("invalid io write.")
				}
			}

			if ew != nil {
				return ew
			}

			if nr != nw {
				return io.ErrShortWrite
			}

		}

		if er != nil {
			if er == io.EOF {
				break
			} else {
				return er
			}
		}
	}

	return nil
}

func (zk *Zapk) AddFromBytes(fname string, dat []byte) error {

	/**/
	dst, err := zk.create(fname)
	if err != nil {
		return err
	}

	/**/
	_, err = dst.Write(dat)
	if err != nil {
		return err
	}

	return nil
}

/*
 */
func (zk *Zapk) Close() error {
	return zk.zw.Close()
}
